﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Security;
using Microsoft.SharePoint;
using System.Collections;
using System.IO;
using MigrateAllPublicFolder;
using MigrateAllPublicFolders;

namespace MigrateAllPublicFolder
{
    public partial class Form1 : Form
    {
        Outlook.MAPIFolder AllPublicFolders;
        SPWeb SPWDestination;
        SPList SPLEmail;

        //DEBUG VARIABLES//
        bool DEBUG = false;

        string FolderDebug = "FOLDER";
        string PublicFolderDebug = "PUBLIC FOLDER NAME";
        int ItemStartDebug = 7346;
        // //

        public Form1()
        {
            InitializeComponent();

            LBLLoading.Text = "";
            LBLTotal.Text = "";
        }


        /// <summary>
        /// CLICK LOAD PUBLIC FOLDERS
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BTNLoadPublicFolders_Click(object sender, EventArgs e)
        {
            BTNLoadPublicFolders.Enabled = false;
            LBLLoading.Text = "Loading public folders...";

            //open outlook application
            Outlook._Application OutlookApp = new Outlook.ApplicationClass();
            Outlook._NameSpace OutlookNs = OutlookApp.GetNamespace("MAPI");
            AllPublicFolders = OutlookNs.GetDefaultFolder(Outlook.OlDefaultFolders.olPublicFoldersAllPublicFolders);

            //load public folders
            int FolderCount = AllPublicFolders.Folders.Count;
            for (int i = 1; i < FolderCount - 1; i++)
            {
                progressBar1.Maximum = FolderCount;

                Outlook.MAPIFolder CurrentFolder = AllPublicFolders.Folders[i];
                CBFolders.Items.Add(CurrentFolder.Name);

                progressBar1.Value = i;
            }

            if (DEBUG)//if debug pre-select debug public folder
            {
                for (int i = 0; i < CBFolders.Items.Count; i++)
                {
                    string s = CBFolders.Items[i].ToString();
                    if (CBFolders.Items[i].ToString() == PublicFolderDebug)
                        CBFolders.SelectedIndex = i;
                }
            }
            else
            {
                CBFolders.SelectedIndex = 0;
            }

            progressBar1.Value = 0;
            LBLLoading.Text = "";
            BTNMigrate.Enabled = true;
        }


        /// <summary>
        /// CLICK MIGRATION BUTTON
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BTNMigrate_Click(object sender, EventArgs e)
        {
            if ((CBCreateFolder.Checked) && (TBExport.Text == ""))
            {
                MessageBox.Show("To create specific sharepoint folder, must select a PFDAVAdmin export file");
            }
            else if ((TBFolderDestUrl.Text == "") || (TBMailLibraryName.Text == ""))
            {
                MessageBox.Show("Specify all destination required parameters");
            }
            else
            {
                try
                {
                    WriteToLog("Opening destination Sharepoint site collection...", TBActivityLog);

                    BTNMigrate.Enabled = false;

                    int Index = CBFolders.SelectedIndex + 1;

                    //create folder and subfolders
                    OpenDestinationLibrary(TBFolderDestUrl.Text);

                    WriteToLog("Opened destination site collection", TBActivityLog);

                    //check if columns are configured for export, otherwise create columns
                    if (!IsLibraryConfigured(SPLEmail))
                        ConfigureLibraryColumns(SPLEmail);
                    
                    //create subfolders
                    int SubFoldersCount = AllPublicFolders.Folders[Index].Folders.Count;

                    for (int i = 1; i <= SubFoldersCount; i++)
                    {
                        CreateFolderAndSubfolders(i, AllPublicFolders.Folders[Index]);
                    }

                    MessageBox.Show("Exported public folder into sharepoint");

                    BTNMigrate.Enabled = false;
                }
                catch (Exception)
                {
                    BTNMigrate.Enabled = true;
                }
            }
        }


        /// <summary>
        /// Assign permissions to specified folder on sharepoint using the PFDAV export file
        /// </summary>
        /// <param name="SPFolderPath"></param>
        public void AssignPermissions(string PFName, string SPFolderPath)
        {
            ArrayList Permissions = new ArrayList();
            StreamReader sr = new StreamReader(TBExport.Text);

            string line;
	        while ((line = sr.ReadLine()) != null)
	        {
                if (line.Contains(PFName))
                {
		            //get an array of permissions from the export line
                    Permissions = GetFolderPermissions(line);
                    break;
                }
	        }

            sr.Close();

            //get sharepoint folder as splistitem
            SPListItem FolderItem = SPWDestination.GetFolder(SPFolderPath).Item;

            //break permissions inheritance
            FolderItem.BreakRoleInheritance(false);
            FolderItem.Update();

            //remove all permissions
            while (FolderItem.RoleAssignments.Count != 0)
                FolderItem.RoleAssignments.Remove(0);

            //assign only key users to folder permissions
            SPRoleAssignment KURoleAssignment = new SPRoleAssignment(SPWDestination.Groups["Key Users"]);
            KURoleAssignment.RoleDefinitionBindings.Add(SPWDestination.RoleDefinitions["MT Key Users"]);
            FolderItem.RoleAssignments.Add(KURoleAssignment);
            //

            //assign other permissions
            for (int i = 0; i < Permissions.Count; i++)
            {
                string UserLogin = Permissions[i].ToString().Split('#')[0];
                string UserPerms = Permissions[i].ToString().Split('#')[1];

                try
                {
                    SPUser user = SPWDestination.EnsureUser(UserLogin);

                    SPRoleAssignment RoleAssignment = new SPRoleAssignment(UserLogin, SPWDestination.AllUsers[UserLogin].Email, SPWDestination.AllUsers[UserLogin].Name, "");
                    RoleAssignment.RoleDefinitionBindings.Add(SPWDestination.RoleDefinitions[GetPermissionLevel(UserPerms)]);
                    FolderItem.RoleAssignments.Add(RoleAssignment);
                }
                catch (Exception) 
                {
                    WriteToLog("Assign permission on folder " + FolderItem.Url + " : cannot find user with login name " + UserLogin, TBExceptionLog);
                }
            }

            FolderItem.Update();
            //
        }


        /// <summary>
        /// Get folder permissions from export line
        /// </summary>
        /// <param name="ExportLine"></param>
        /// <returns></returns>
        public static ArrayList GetFolderPermissions(string ExportLine)
        {
            ArrayList Permissions = new ArrayList();
            
            string[] permissionsarray = ExportLine.Split('\t');

            for (int i = 2; i < permissionsarray.Length - 3; i += 2)
            {
                if (permissionsarray[i] != "")
                {
                    string user = permissionsarray[i].Replace("\\", "");
                    if (user == "Everyone") user = "NT AUTHORITY\\authenticated users";

                    if (!user.Contains("Administrator") && !user.Contains("ANONYMOUS"))
                    {
                        string perm = permissionsarray[i + 1].Replace("\\", "");

                        Permissions.Add(permissionsarray[i] + "#" + permissionsarray[i + 1]);
                    }
                }
            }

            return Permissions;
        }


        /// <summary>
        /// Translate exchange permissions into sharepoint permissions level
        /// </summary>
        /// <param name="Permission"></param>
        /// <returns></returns>
        public static string GetPermissionLevel(string Permission)
        {
            string PermissionLevel = "";

            switch (Permission)
            {
                case "All": PermissionLevel = "MT Key Users"; break;
                case "Editor": PermissionLevel = "MT Contributors"; break;
                case "Reviewer": PermissionLevel = "MT Readers"; break;
            }

            return PermissionLevel;
        }

        
        /// <summary>
        /// Open site collection destionation web
        /// </summary>
        /// <param name="SiteDestinationPath"></param>
        public void OpenDestinationLibrary(string SiteDestinationPath)
        {
            try
            {
                SPWDestination = new SPSite(SiteDestinationPath).OpenWeb();
                SPLEmail = SPWDestination.Lists[TBMailLibraryName.Text];
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message, TBExceptionLog);
            }
        }


        /// <summary>
        /// Create folder and subfolders recursively
        /// </summary>
        /// <param name="FolderIndex"></param>
        /// <param name="ParentFolder"></param>
        public void CreateFolderAndSubfolders(int FolderIndex, Outlook.MAPIFolder ParentFolder)
        {
            bool SpecificFolder = CBCreateFolder.Checked;

            string SPParentFolderPath = MyUtility.GetRelativePFPath(ParentFolder.FolderPath);
            SPFolder SPParentFolder = SPWDestination.GetFolder(SPWDestination.Url + "/" + TBMailLibraryName.Text + SPParentFolderPath);
            
            string NewFolderName = MyUtility.GetStringAllowedChars(ParentFolder.Folders[FolderIndex].Name);

            if (!NewFolderName.Contains("@"))
            {
                SPParentFolder.SubFolders.Add(NewFolderName);

                if (CBCreateFolder.Checked)
                {
                    string SPNewFolderPath = SPWDestination.Url + "/" + SPParentFolder.Url + "/" + NewFolderName;
                    AssignPermissions(ParentFolder.Folders[FolderIndex].Name, SPNewFolderPath);
                }

                WriteToLog("Created folder " + SPParentFolderPath + "/" + NewFolderName, TBActivityLog);

                //copy folder's emails
                if (!CBStructure.Checked)
                {
                    //NEXT ROW ONLY FOR DEBUG
                    if ((!DEBUG) || ((DEBUG) && (ParentFolder.Folders[FolderIndex].Name == FolderDebug)))
                    {
                        DEBUG = false;//in debbugging mode, start copy mail when reache folder debug

                        CopyFolderMails(ParentFolder.Folders[FolderIndex], SPParentFolderPath + "/" + NewFolderName);
                    }

                    WriteToLog("Migrated folder's items", TBActivityLog);
                }

                //create subfolders
                int subFoldersCount = ParentFolder.Folders[FolderIndex].Folders.Count;

                if (subFoldersCount > 0)
                {
                    for (int i = 1; i <= subFoldersCount; i++)
                    {
                        CreateFolderAndSubfolders(i, ParentFolder.Folders[FolderIndex]);
                    }
                }
            }
        }


        /// <summary>
        /// Copy emails of one outlook folder into sharepoint
        /// </summary>
        /// <param name="Folder"></param>
        /// <param name="FolderPath"></param>
        public void CopyFolderMails(Outlook.MAPIFolder Folder, string FolderPath)
        {
            //check if folder contains emails
            int FilesCount = Folder.Items.Count;

            if (FilesCount > 0)
            {
                LBLLoading.Text = "Migrating all items (" + FilesCount + ") of folder " + FolderPath;
                progressBar1.Maximum = FilesCount;

                for (int i = 1; i <= FilesCount; i++)
                {
                    if ((!DEBUG) || ((DEBUG) && (i > ItemStartDebug)))
                    {
                        //DEBUG = false;//in debbugging mode, start copy mail when reache folder debug

                        OutlookMail Mail = new OutlookMail(Folder.Items[i]);

                        if (Mail.Created)
                        {
                            string ItemPath = "";

                            //create formatted filename from subject
                            string Filename = MyUtility.GetStringAllowedChars(MyUtility.GetMax100chars(Mail.Subject));
                            Filename += "_" + MyUtility.GenerateExadecimalCode(4) + ".msg";

                            ItemPath = SPWDestination.Url + "/" + TBMailLibraryName.Text + FolderPath + "/" + Filename;

                            try//try save mail on sharepoint with formatted filename
                            {
                                Mail.SaveAs(ItemPath);
                            }
                            catch//failed save with formatted filename
                            {
                                //generate an alphanumeric code for the sharepoint filename
                                Filename = MyUtility.GenerateExadecimalCode(30) + ".msg";

                                string message = "Can't save mail n° << " + i + " >> received time << " + Mail.Received + " >>" +
                                                    " location << " + FolderPath + " >> subject << " + Mail.Subject + 
                                                    " >> email will be saved with filename << " + Filename + " >>";

                                WriteToLog(message, TBExceptionLog);

                                ItemPath = SPWDestination.Url + "/" + TBMailLibraryName.Text + FolderPath + "/" + Filename;
                                Mail.SaveAs(ItemPath);
                            }

                            try//save metadata on sharepoint item
                            {
                                SaveSharepointMetadata(Folder.Items[i], ItemPath);
                            }
                            catch//failed save sharepoint metadata
                            {
                                WriteToLog("Can't save metadata for mail n° << " + i + " >> subject: << " + FolderPath + "/" + Mail.Subject + " >>", TBExceptionLog);
                            }

                            GC.Collect();
                            GC.WaitForPendingFinalizers();
                            GC.Collect();
                            GC.WaitForPendingFinalizers();

                            progressBar1.Value = i;
                        }
                        else
                        {
                            WriteToLog("Can't define type of outlook item n° << " + i + " >> folder << " + Folder.FolderPath + " >>", TBExceptionLog);
                        }
                    }
                }

                progressBar1.Value = 0;
                LBLLoading.Text = "";
            }
        }        


        /// <summary>
        /// Save mail metadata on sharepoint
        /// </summary>
        /// <param name="Mail"></param>
        /// <param name="ItemPath"></param>
        public void SaveSharepointMetadata(object OutlookItem, string ItemPath)
        {
            SPListItem spMail = SPWDestination.GetListItem(ItemPath);
            OutlookMail Mail = new OutlookMail(OutlookItem);

            try
            {
                string title = MyUtility.GetMax250chars(Mail.Subject);
                if (title.EndsWith("."))
                    title = title.Remove(title.Length-1);
                spMail["Title"] = title;

                spMail["From"] = Mail.From;
                spMail["To"] = Mail.To;
                spMail["Received"] = Mail.Received;
                spMail["Attach"] = Mail.Attach;
                spMail["FlagStatus"] = Mail.FlagStatus;

                spMail.Update();
            }
            catch
            {
                WriteToLog("Cant save metadata for item " + Mail.Subject, TBExceptionLog);
            }
        }


        /// <summary>
        /// Check if destination document library have all columns configured for export
        /// </summary>
        /// <param name="Library"></param>
        /// <returns></returns>
        public bool IsLibraryConfigured(SPList Library)
        {
            bool result = true;
            SPViewFieldCollection AllDocsViewFields = Library.Views["All Documents"].ViewFields;
            ArrayList CheckFields = new ArrayList { "Attach", "From", "To", "Received", "FlagStatus" };

            ArrayList ViewFields = new ArrayList();
            for (int i = 0; i < AllDocsViewFields.Count; i++)
                ViewFields.Add(AllDocsViewFields[i]);

            for (int i = 0; i < CheckFields.Count; i++)
            {
                if (!ViewFields.Contains(CheckFields[i]))
                {
                    result = false;
                    break;
                }
            }

            return result;
        }


        /// <summary>
        /// Create columns for mail metadata if dont exist on sharepoint document library
        /// </summary>
        /// <param name="Library"></param>
        public void ConfigureLibraryColumns(SPList Library)
        {
            ArrayList NewFields = new ArrayList { "Attach", "From", "To", "Received", "FlagStatus" };
            ArrayList LibraryFields = new ArrayList();
            SPView AllDocsView = Library.Views["All Documents"];

            //get library columns
            foreach (SPField Field in Library.Fields)
                LibraryFields.Add(Field.Title);

            for (int i = 0; i < NewFields.Count; i++)
            {
                string Name = NewFields[i].ToString();

                //create new column in the library if not exists
                if (!LibraryFields.Contains(Name))
                {
                    SPFieldType FieldType = SPFieldType.Text;

                    if (Name == "Received")//field "Received"
                        FieldType = SPFieldType.DateTime;

                    //create new field
                    Library.Fields.Add(Name, FieldType, false, false, null);

                    //add field to default view
                    AllDocsView.ViewFields.Add(Library.Fields[Name]);
                    AllDocsView.Update();
                }
            }

            //get default view's columns
            ArrayList AllDocsViewFields = new ArrayList();
            foreach (String Field in AllDocsView.ViewFields)
                AllDocsViewFields.Add(Field);

            //rename Title column
            SPField TitleField = Library.Fields.GetFieldByInternalName("Title");
            TitleField.Title = "Subject";
            TitleField.Update(true);

            //add title and size to the default view
            if (!AllDocsViewFields.Contains("Subject")) { AllDocsView.ViewFields.Add(Library.Fields["Subject"]); AllDocsView.Update(); }
            if (!AllDocsViewFields.Contains("File_x0020_Size")) { AllDocsView.ViewFields.Add(Library.Fields["File Size"]); AllDocsView.Update(); }

            //remove modified and modified by
            if (AllDocsViewFields.Contains("Modified")) AllDocsView.ViewFields.Delete(Library.Fields["Modified"]); AllDocsView.Update();
            if (AllDocsViewFields.Contains("Editor")) AllDocsView.ViewFields.Delete(Library.Fields["Modified By"]); AllDocsView.Update();
            
            //order view columns
            ArrayList FieldOrder = new ArrayList { "1#DocIcon", "2#Attach", "3#LinkFilename", "4#From", "5#To", "6#Title", "7#Received", "8#File_x0020_Size", "9#FlagStatus" };

            for (int j = 0; j < FieldOrder.Count; j++)
            {
                string FieldName = FieldOrder[j].ToString().Split('#')[1];
                int Order = int.Parse(FieldOrder[j].ToString().Split('#')[0]);
                AllDocsView.ViewFields.MoveFieldTo(FieldName, Order);
            }
        }


        /// <summary>
        /// Write a message to specified textbox
        /// </summary>
        /// <param name="Message"></param>
        /// <param name="Log"></param>
        public void WriteToLog(string Message, TextBox Log)
        {
            Log.AppendText(MyUtility.GetCurrentDateAndTime() + " \t " + Message);
            Log.AppendText(Environment.NewLine);
            
        }


        /// <summary>
        /// CLICK COPY TO CLIPBOARD
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BTNClipboard_Click(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(TBActivityLog.Text, true);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(TBExceptionLog.Text, true);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            openFileDialog1.ShowDialog();
        }

        private void CBCreateFolder_MouseClick(object sender, MouseEventArgs e)
        {
            if (CBCreateFolder.Checked)
                PNLExport.Visible = true;
            else
                PNLExport.Visible = false;
        }

        private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
        {
            TBExport.Text = openFileDialog1.FileName;
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            AboutBox1 ab = new AboutBox1();
            ab.Show();
        }

    }
}